package tool

import (
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// 文件存储配置
const (
	// 上传文件存储目录
	UploadDir = "uploads"
	// 临时文件存储目录
	TempDir = "tmp/uploads"
	// 默认允许的最大文件大小 (10MB)
	DefaultMaxFileSize = 10 << 20
)

// 允许的文件类型
var AllowedFileTypes = map[string]bool{
	".jpg":  true,
	".jpeg": true,
	".png":  true,
	".gif":  true,
	".pdf":  true,
	".doc":  true,
	".docx": true,
	".xls":  true,
	".xlsx": true,
	".zip":  true,
	".txt":  true,
}

// SaveUploadedFile 保存上传的文件到指定目录
func SaveUploadedFile(file *multipart.FileHeader, dir string) (string, error) {
	// 检查文件大小
	if file.Size > DefaultMaxFileSize {
		return "", errors.New("文件大小超过限制")
	}

	// 检查文件类型
	ext := strings.ToLower(filepath.Ext(file.Filename))
	if !AllowedFileTypes[ext] {
		return "", errors.New("不支持的文件类型")
	}

	// 创建目录
	if err := os.MkdirAll(dir, 0755); err != nil {
		return "", err
	}

	// 生成唯一文件名
	filename := uuid.New().String() + ext
	filePath := filepath.Join(dir, filename)

	// 打开源文件
	src, err := file.Open()
	if err != nil {
		return "", err
	}
	defer src.Close()

	// 创建目标文件
	dst, err := os.Create(filePath)
	if err != nil {
		return "", err
	}
	defer dst.Close()

	// 复制文件内容
	if _, err = io.Copy(dst, src); err != nil {
		return "", err
	}

	return filename, nil
}

// HandleFileUpload 处理文件上传请求
func HandleFileUpload(ctx *gin.Context, subDir string, allowedTypes map[string]bool) (string, error) {
	// 获取上传的文件
	file, err := ctx.FormFile("file")
	if err != nil {
		return "", err
	}

	// 如果提供了自定义的文件类型限制，则使用它
	fileTypes := AllowedFileTypes
	if allowedTypes != nil && len(allowedTypes) > 0 {
		fileTypes = allowedTypes
	}

	// 检查文件类型
	ext := strings.ToLower(filepath.Ext(file.Filename))
	if !fileTypes[ext] {
		return "", errors.New("不支持的文件类型")
	}

	// 确定存储目录
	uploadPath := UploadDir
	if subDir != "" {
		uploadPath = filepath.Join(uploadPath, subDir)
	}

	// 按年月日组织文件
	now := time.Now()
	datePath := filepath.Join(fmt.Sprintf("%d", now.Year()), fmt.Sprintf("%02d", now.Month()), fmt.Sprintf("%02d", now.Day()))
	uploadPath = filepath.Join(uploadPath, datePath)

	// 保存文件
	filename, err := SaveUploadedFile(file, uploadPath)
	if err != nil {
		return "", err
	}

	// 返回文件的相对路径
	return filepath.Join(datePath, filename), nil
}

// DownloadFile 处理文件下载请求
func DownloadFile(ctx *gin.Context, filePath string, downloadName string) error {
	// 规范化文件路径，防止目录遍历攻击
	cleanPath := filepath.Clean(filePath)
	if strings.HasPrefix(cleanPath, "..") || strings.Contains(cleanPath, "../") {
		return errors.New("无效的文件路径")
	}

	// 确定完整的文件路径
	fullPath := filepath.Join(UploadDir, cleanPath)

	// 检查文件是否存在
	if _, err := os.Stat(fullPath); os.IsNotExist(err) {
		return errors.New("文件不存在")
	}

	// 如果没有指定下载文件名，使用原始文件名
	if downloadName == "" {
		downloadName = filepath.Base(fullPath)
	}

	// 设置响应头
	ctx.Header("Content-Description", "File Transfer")
	ctx.Header("Content-Type", "application/octet-stream")
	ctx.Header("Content-Disposition", "attachment; filename="+downloadName)
	ctx.Header("Content-Transfer-Encoding", "binary")
	ctx.Header("Expires", "0")
	ctx.Header("Cache-Control", "private, no-transform, no-store, must-revalidate")
	ctx.Header("Pragma", "no-cache")

	// 提供文件下载
	ctx.File(fullPath)
	return nil
}

// RemoveFile 删除上传的文件
func RemoveFile(filePath string) error {
	// 规范化文件路径，防止目录遍历攻击
	cleanPath := filepath.Clean(filePath)
	if strings.HasPrefix(cleanPath, "..") || strings.Contains(cleanPath, "../") {
		return errors.New("无效的文件路径")
	}

	// 确定完整的文件路径
	fullPath := filepath.Join(UploadDir, cleanPath)

	// 检查文件是否存在
	if _, err := os.Stat(fullPath); os.IsNotExist(err) {
		return errors.New("文件不存在")
	}

	// 删除文件
	return os.Remove(fullPath)
}

// CreateTempFile 创建临时文件并写入内容
func CreateTempFile(content []byte, fileExt string) (string, string, error) {
	// 创建临时目录
	if err := os.MkdirAll(TempDir, 0755); err != nil {
		return "", "", err
	}

	// 生成唯一文件ID和名称
	fileID := uuid.New().String()
	fileName := fileID
	if fileExt != "" {
		if !strings.HasPrefix(fileExt, ".") {
			fileExt = "." + fileExt
		}
		fileName = fileID + fileExt
	}

	// 创建临时文件路径
	filePath := filepath.Join(TempDir, fileName)

	// 写入文件内容
	if err := os.WriteFile(filePath, content, 0644); err != nil {
		return "", "", err
	}

	return fileID, fileName, nil
}

// CleanupTempFiles 清理过期的临时文件
func CleanupTempFiles(maxAge time.Duration) error {
	return filepath.Walk(TempDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 跳过目录
		if info.IsDir() {
			return nil
		}

		// 检查文件是否过期
		if time.Since(info.ModTime()) > maxAge {
			if err := os.Remove(path); err != nil {
				// 记录错误但继续处理其他文件
				fmt.Printf("删除临时文件失败: %s, 错误: %v\n", path, err)
			}
		}

		return nil
	})
}

// EnsureDir 确保目录存在，如果不存在则创建
func EnsureDir(dir string) error {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		return os.MkdirAll(dir, 0755)
	}
	return nil
}
